return value->value;
}
+/* GtkCssBlendMode */
+
+static const GtkCssValueClass GTK_CSS_VALUE_BLEND_MODE = {
+ gtk_css_value_enum_free,
+ gtk_css_value_enum_compute,
+ gtk_css_value_enum_equal,
+ gtk_css_value_enum_transition,
+ gtk_css_value_enum_print
+};
+
+static GtkCssValue blend_mode_values[] = {
+ { >K_CSS_VALUE_BLEND_MODE, 1, GTK_CSS_BLEND_MODE_COLOR, "color" },
+ { >K_CSS_VALUE_BLEND_MODE, 1, GTK_CSS_BLEND_MODE_COLOR_BURN, "color-burn" },
+ { >K_CSS_VALUE_BLEND_MODE, 1, GTK_CSS_BLEND_MODE_COLOR_DODGE, "color-dodge" },
+ { >K_CSS_VALUE_BLEND_MODE, 1, GTK_CSS_BLEND_MODE_DARKEN, "darken" },
+ { >K_CSS_VALUE_BLEND_MODE, 1, GTK_CSS_BLEND_MODE_DIFFERENCE, "difference" },
+ { >K_CSS_VALUE_BLEND_MODE, 1, GTK_CSS_BLEND_MODE_EXCLUSION, "exclusion" },
+ { >K_CSS_VALUE_BLEND_MODE, 1, GTK_CSS_BLEND_MODE_HARD_LIGHT, "hard-light" },
+ { >K_CSS_VALUE_BLEND_MODE, 1, GTK_CSS_BLEND_MODE_HUE, "hue" },
+ { >K_CSS_VALUE_BLEND_MODE, 1, GTK_CSS_BLEND_MODE_LIGHTEN, "lighten" },
+ { >K_CSS_VALUE_BLEND_MODE, 1, GTK_CSS_BLEND_MODE_LUMINOSITY, "luminosity" },
+ { >K_CSS_VALUE_BLEND_MODE, 1, GTK_CSS_BLEND_MODE_MULTIPLY, "multiply" },
+ { >K_CSS_VALUE_BLEND_MODE, 1, GTK_CSS_BLEND_MODE_NORMAL, "normal" },
+ { >K_CSS_VALUE_BLEND_MODE, 1, GTK_CSS_BLEND_MODE_OVERLAY, "overlay" },
+ { >K_CSS_VALUE_BLEND_MODE, 1, GTK_CSS_BLEND_MODE_SATURATE, "saturate" },
+ { >K_CSS_VALUE_BLEND_MODE, 1, GTK_CSS_BLEND_MODE_SCREEN, "screen" },
+ { >K_CSS_VALUE_BLEND_MODE, 1, GTK_CSS_BLEND_MODE_SOFT_LIGHT, "soft-light" }
+};
+
+GtkCssValue *
+_gtk_css_blend_mode_value_new (GtkCssBlendMode blend_mode)
+{
+ g_return_val_if_fail (blend_mode < G_N_ELEMENTS (blend_mode_values), NULL);
+
+ return _gtk_css_value_ref (&blend_mode_values[blend_mode]);
+}
+
+GtkCssValue *
+_gtk_css_blend_mode_value_try_parse (GtkCssParser *parser)
+{
+ guint i;
+
+ g_return_val_if_fail (parser != NULL, NULL);
+
+ for (i = 0; i < G_N_ELEMENTS (blend_mode_values); i++)
+ {
+ if (_gtk_css_parser_try (parser, blend_mode_values[i].name, TRUE))
+ return _gtk_css_value_ref (&blend_mode_values[i]);
+ }
+
+ return NULL;
+}
+
+GtkCssBlendMode
+_gtk_css_blend_mode_value_get (const GtkCssValue *value)
+{
+ g_return_val_if_fail (value->class == >K_CSS_VALUE_BLEND_MODE, GTK_CSS_BLEND_MODE_NORMAL);
+
+ return value->value;
+}
+
/* GtkCssFontSize */
static double
G_BEGIN_DECLS
+GtkCssValue * _gtk_css_blend_mode_value_new (GtkCssBlendMode blend_mode);
+GtkCssValue * _gtk_css_blend_mode_value_try_parse (GtkCssParser *parser);
+GtkCssBlendMode _gtk_css_blend_mode_value_get (const GtkCssValue *value);
+
GtkCssValue * _gtk_css_border_style_value_new (GtkBorderStyle border_style);
GtkCssValue * _gtk_css_border_style_value_try_parse (GtkCssParser *parser);
GtkBorderStyle _gtk_css_border_style_value_get (const GtkCssValue *value);
| GTK_CSS_PARSE_LENGTH);
}
+static GtkCssValue *
+blend_mode_value_parse_one (GtkCssParser *parser)
+{
+ GtkCssValue *value = _gtk_css_blend_mode_value_try_parse (parser);
+
+ if (value == NULL)
+ _gtk_css_parser_error (parser, "unknown value for property");
+
+ return value;
+}
+
+static GtkCssValue *
+blend_mode_value_parse (GtkCssStyleProperty *property,
+ GtkCssParser *parser)
+{
+ return _gtk_css_array_value_parse (parser, blend_mode_value_parse_one);
+}
+
static GtkCssValue *
background_repeat_value_parse_one (GtkCssParser *parser)
{
background_image_value_assign,
_gtk_css_array_value_new (_gtk_css_image_value_new (NULL)));
+ gtk_css_style_property_register ("background-blend-mode",
+ GTK_CSS_PROPERTY_BACKGROUND_BLEND_MODE,
+ G_TYPE_NONE,
+ 0,
+ GTK_CSS_AFFECTS_BACKGROUND,
+ blend_mode_value_parse,
+ NULL,
+ NULL,
+ _gtk_css_array_value_new (_gtk_css_blend_mode_value_new (GTK_CSS_BLEND_MODE_NORMAL)));
+
gtk_css_style_property_register ("border-image-source",
GTK_CSS_PROPERTY_BORDER_IMAGE_SOURCE,
CAIRO_GOBJECT_TYPE_PATTERN,
#include "gtkcssnumbervalueprivate.h"
#include "gtkstylecontextprivate.h"
+cairo_operator_t
+_gtk_css_blend_mode_get_operator (GtkCssBlendMode mode)
+{
+ switch (mode)
+ {
+ case GTK_CSS_BLEND_MODE_COLOR:
+ return CAIRO_OPERATOR_HSL_COLOR;
+ case GTK_CSS_BLEND_MODE_COLOR_BURN:
+ return CAIRO_OPERATOR_COLOR_BURN;
+ case GTK_CSS_BLEND_MODE_COLOR_DODGE:
+ return CAIRO_OPERATOR_COLOR_DODGE;
+ case GTK_CSS_BLEND_MODE_DARKEN:
+ return CAIRO_OPERATOR_DARKEN;
+ case GTK_CSS_BLEND_MODE_DIFFERENCE:
+ return CAIRO_OPERATOR_DIFFERENCE;
+ case GTK_CSS_BLEND_MODE_EXCLUSION:
+ return CAIRO_OPERATOR_EXCLUSION;
+ case GTK_CSS_BLEND_MODE_HARD_LIGHT:
+ return CAIRO_OPERATOR_HARD_LIGHT;
+ case GTK_CSS_BLEND_MODE_HUE:
+ return CAIRO_OPERATOR_HSL_HUE;
+ case GTK_CSS_BLEND_MODE_LIGHTEN:
+ return CAIRO_OPERATOR_LIGHTEN;
+ case GTK_CSS_BLEND_MODE_LUMINOSITY:
+ return CAIRO_OPERATOR_HSL_LUMINOSITY;
+ case GTK_CSS_BLEND_MODE_MULTIPLY:
+ return CAIRO_OPERATOR_MULTIPLY;
+ case GTK_CSS_BLEND_MODE_OVERLAY:
+ return CAIRO_OPERATOR_OVERLAY;
+ case GTK_CSS_BLEND_MODE_SATURATE:
+ return CAIRO_OPERATOR_SATURATE;
+ case GTK_CSS_BLEND_MODE_SCREEN:
+ return CAIRO_OPERATOR_SCREEN;
+
+ case GTK_CSS_BLEND_MODE_NORMAL:
+ default:
+ return CAIRO_OPERATOR_OVER;
+ }
+}
+
GtkCssChange
_gtk_css_change_for_sibling (GtkCssChange match)
{
GTK_CSS_PROPERTY_OUTLINE_COLOR,
GTK_CSS_PROPERTY_BACKGROUND_REPEAT,
GTK_CSS_PROPERTY_BACKGROUND_IMAGE,
+ GTK_CSS_PROPERTY_BACKGROUND_BLEND_MODE,
GTK_CSS_PROPERTY_BORDER_IMAGE_SOURCE,
GTK_CSS_PROPERTY_BORDER_IMAGE_REPEAT,
GTK_CSS_PROPERTY_BORDER_IMAGE_SLICE,
GTK_CSS_PROPERTY_N_PROPERTIES
};
+typedef enum /*< skip >*/ {
+ GTK_CSS_BLEND_MODE_COLOR,
+ GTK_CSS_BLEND_MODE_COLOR_BURN,
+ GTK_CSS_BLEND_MODE_COLOR_DODGE,
+ GTK_CSS_BLEND_MODE_DARKEN,
+ GTK_CSS_BLEND_MODE_DIFFERENCE,
+ GTK_CSS_BLEND_MODE_EXCLUSION,
+ GTK_CSS_BLEND_MODE_HARD_LIGHT,
+ GTK_CSS_BLEND_MODE_HUE,
+ GTK_CSS_BLEND_MODE_LIGHTEN,
+ GTK_CSS_BLEND_MODE_LUMINOSITY,
+ GTK_CSS_BLEND_MODE_MULTIPLY,
+ GTK_CSS_BLEND_MODE_NORMAL,
+ GTK_CSS_BLEND_MODE_OVERLAY,
+ GTK_CSS_BLEND_MODE_SATURATE,
+ GTK_CSS_BLEND_MODE_SCREEN,
+ GTK_CSS_BLEND_MODE_SOFT_LIGHT
+} GtkCssBlendMode;
+
typedef enum /*< skip >*/ {
GTK_CSS_IMAGE_BUILTIN_NONE,
GTK_CSS_IMAGE_BUILTIN_CHECK,
GTK_CSS_MS,
} GtkCssUnit;
+cairo_operator_t _gtk_css_blend_mode_get_operator (GtkCssBlendMode mode);
+
GtkCssChange _gtk_css_change_for_sibling (GtkCssChange match);
GtkCssChange _gtk_css_change_for_child (GtkCssChange match);
cairo_fill (cr);
}
+static gboolean
+_gtk_theming_background_needs_push_group (GtkCssStyle *style)
+{
+ const GdkRGBA *bg_color;
+ GtkCssValue *background_color;
+ GtkCssValue *blend_modes;
+ gint i;
+
+ background_color = gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BACKGROUND_COLOR);
+ bg_color = _gtk_css_rgba_value_get_rgba (background_color);
+
+ /* An opaque background-color means we don't need to push the group */
+ if (bg_color->alpha == 1)
+ return FALSE;
+
+ blend_modes = gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BACKGROUND_BLEND_MODE);
+
+ /*
+ * If we have any blend mode different than NORMAL, we'll need to
+ * push a group in order to correctly apply the blend modes.
+ */
+ for (i = _gtk_css_array_value_get_n_values (blend_modes); i > 0; i--)
+ {
+ GtkCssBlendMode blend_mode;
+
+ blend_mode = _gtk_css_blend_mode_value_get (_gtk_css_array_value_get_nth (blend_modes, i - 1));
+
+ if (blend_mode != GTK_CSS_BLEND_MODE_NORMAL)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
static void
_gtk_theming_background_paint_layer (GtkThemingBackground *bg,
guint idx,
- cairo_t *cr)
+ cairo_t *cr,
+ GtkCssBlendMode blend_mode)
{
GtkCssRepeatStyle hrepeat, vrepeat;
const GtkCssValue *pos, *repeat;
cairo_translate (cr, origin->box.x, origin->box.y);
+ /*
+ * Apply the blend mode, if any.
+ */
+ if (G_UNLIKELY (_gtk_css_blend_mode_get_operator (blend_mode) != cairo_get_operator (cr)))
+ cairo_set_operator (cr, _gtk_css_blend_mode_get_operator (blend_mode));
+
+
if (hrepeat == GTK_CSS_REPEAT_STYLE_NO_REPEAT && vrepeat == GTK_CSS_REPEAT_STYLE_NO_REPEAT)
{
cairo_translate (cr,
cairo_fill (cr);
}
+ /*
+ * Since this cairo_t can be shared with other widgets,
+ * we must reset the operator after all the backgrounds
+ * are properly rendered.
+ */
+ cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
cairo_restore (cr);
}
GtkThemingBackground bg;
gint idx;
GtkCssValue *background_image;
+ GtkCssValue *blend_modes;
GtkCssValue *box_shadow;
const GdkRGBA *bg_color;
+ gboolean needs_push_group;
+ gint number_of_layers;
background_image = gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BACKGROUND_IMAGE);
+ blend_modes = gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BACKGROUND_BLEND_MODE);
bg_color = _gtk_css_rgba_value_get_rgba (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BACKGROUND_COLOR));
box_shadow = gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BOX_SHADOW);
cairo_save (cr);
cairo_translate (cr, x, y);
+ /*
+ * When we have a blend mode set for the background, we cannot blend the current
+ * widget's drawing with whatever the content that the Cairo context may have.
+ * Because of that, push the drawing to a new group before drawing the background
+ * layers, and paint the resulting image back after.
+ */
+ needs_push_group = _gtk_theming_background_needs_push_group (style);
+
+ if (needs_push_group)
+ {
+ cairo_save (cr);
+ cairo_rectangle (cr, x, y, width, height);
+ cairo_clip (cr);
+ cairo_push_group (cr);
+ }
+
/* Outset shadows */
_gtk_css_shadows_value_paint_box (box_shadow,
cr,
_gtk_theming_background_paint_color (&bg, cr, bg_color, background_image);
- for (idx = _gtk_css_array_value_get_n_values (background_image) - 1; idx >= 0; idx--)
+ number_of_layers = _gtk_css_array_value_get_n_values (background_image);
+
+ for (idx = number_of_layers - 1; idx >= 0; idx--)
{
- _gtk_theming_background_paint_layer (&bg, idx, cr);
+ GtkCssBlendMode blend_mode;
+
+ blend_mode = _gtk_css_blend_mode_value_get (_gtk_css_array_value_get_nth (blend_modes, idx));
+
+ _gtk_theming_background_paint_layer (&bg, idx, cr, blend_mode);
}
/* Inset shadows */
&bg.boxes[GTK_CSS_AREA_PADDING_BOX],
TRUE);
+ /* Paint back the resulting surface */
+ if (needs_push_group)
+ {
+ cairo_pop_group_to_source (cr);
+ cairo_paint (cr);
+ cairo_restore (cr);
+ }
+
cairo_restore (cr);
}